package cn.yhbk.web.modules.manager.Service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.yhbk.web.common.Util.MyUtil;
import cn.yhbk.web.common.api.CommonPage;
import cn.yhbk.web.common.api.CommonResult;
import cn.yhbk.web.common.exception.ApiException;
import cn.yhbk.web.modules.manager.Pojo.DTO.Student.StudentImportDto;
import cn.yhbk.web.modules.manager.Pojo.Entity.*;
import cn.yhbk.web.modules.manager.Mapper.TbStuWorkOfficeMapper;
import cn.yhbk.web.modules.manager.Pojo.VO.GpaRecord.GpaRecordAdd;
import cn.yhbk.web.modules.manager.Pojo.VO.StuWorkOffice.StuWorkOfficeAdd;
import cn.yhbk.web.modules.manager.Pojo.VO.StuWorkOffice.StuWorkOfficeResP;
import cn.yhbk.web.modules.manager.Pojo.VO.Student.StudentAdd;
import cn.yhbk.web.modules.manager.Service.*;
import cn.yhbk.web.modules.ums.dto.UmsAdminParam;
import cn.yhbk.web.modules.ums.model.UmsAdmin;
import cn.yhbk.web.modules.ums.service.UmsAdminService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学工办表 服务实现类
 * </p>
 *
 * @author 有何不可
 * @since 2023-08-05
 */
@Service
@Slf4j
public class TbStuWorkOfficeServiceImpl extends ServiceImpl<TbStuWorkOfficeMapper, TbStuWorkOffice> implements ITbStuWorkOfficeService {

    @Autowired
    private UmsAdminService adminService;

    @Autowired
    private IFlagService flagService;

    @Autowired
    private ITbStudentService studentService;

    @Autowired
    private ITbGpaRecordService iTbGpaRecordService;

    @Autowired
    private ITbMajorService majorService;

    @Autowired
    private ITbClassService classService;

    @Autowired
    private ITbCollegeService collegeService;

    @Autowired
    private ITbGradeService iTbGradeService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    @Transactional
    public boolean addStuWorkOffice(StuWorkOfficeAdd add) {

        //添加账号
        UmsAdminParam umsAdminParam = new UmsAdminParam();
        umsAdminParam.setUsername(add.getUsername());
        umsAdminParam.setPassword(add.getPassword());
        UmsAdmin register = adminService.register(umsAdminParam);
        if (Objects.isNull(register)) {
            log.error("添加账号失败!");
            throw new ApiException("添加失败!");
        }
        Long adminId = register.getId();

        //添加信息
        TbStuWorkOffice stuWorkOffice = new TbStuWorkOffice();
        BeanUtil.copyProperties(add, stuWorkOffice);
        stuWorkOffice.setAdminId(adminId);
        boolean save = save(stuWorkOffice);
        if (!save) {
            log.error("添加信息失败!");
            throw new ApiException("添加失败!");
        }


        //设置角色
        Long stuWorkOfficeRoleId = flagService.getStuWorkOfficeRoleId();
        int updateRole = adminService.updateRole(adminId, Collections.singletonList(stuWorkOfficeRoleId));
        if (updateRole == 0) {
            log.error("设置角色失败!");
            throw new ApiException("添加失败!");
        }

        return true;
    }

    @Override
    public CommonPage<StuWorkOfficeResP> getStuWorkOffice(Long currentPage, Long pageSize, Long collegeId) {
        Page<TbStuWorkOffice> page = new Page<>(currentPage, pageSize);

        QueryWrapper<TbStuWorkOffice> wrapper = new QueryWrapper<>();
        LambdaQueryWrapper<TbStuWorkOffice> lambda = wrapper.lambda();

        lambda.eq(TbStuWorkOffice::getCollegeId, collegeId);

        Page<TbStuWorkOffice> dataPage = page(page, lambda);

        CommonPage<TbStuWorkOffice> tmpPage = CommonPage.restPage(dataPage);
        CommonPage<StuWorkOfficeResP> resPage = new CommonPage<>();
        BeanUtil.copyProperties(tmpPage, resPage);

        List<StuWorkOfficeResP> resList = packData(tmpPage.getList());
        resPage.setList(resList);
        return resPage;
    }

    @Override
    @Transactional
    public boolean removeStuWorkOffice(Long stuWorkOfficeId) {
        TbStuWorkOffice stuWorkOffice = getById(stuWorkOfficeId);
        Long adminId = stuWorkOffice.getAdminId();

        //删除账号
        boolean deleteAdmin = adminService.delete(adminId);
        if (!deleteAdmin) {
            log.error("删除账号失败!");
            throw new ApiException("删除失败!");
        }

        //删除信息
        boolean removeStuWorkOffice = removeById(stuWorkOfficeId);
        if (!removeStuWorkOffice) {
            log.error("删除信息失败!");
            throw new ApiException("删除失败!");
        }

        return true;
    }

    private List<StuWorkOfficeResP> packData(List<TbStuWorkOffice> list) {
        if (list.isEmpty()) return new ArrayList<>();

        //拿到所有的账号并且封装成map
        List<Long> adminIds = list.stream().map(TbStuWorkOffice::getAdminId).collect(Collectors.toList());
        List<UmsAdmin> umsAdmins = adminService.listByIds(adminIds);
        Map<Long, String> usernameMap = umsAdmins.stream().collect(Collectors.toMap(UmsAdmin::getId, UmsAdmin::getUsername));
        Map<Long, Integer> statusMap = umsAdmins.stream().collect(Collectors.toMap(UmsAdmin::getId, UmsAdmin::getStatus));

        return list.stream().map(item -> {
            StuWorkOfficeResP tmp = new StuWorkOfficeResP();
            tmp.setUsername(usernameMap.get(item.getAdminId()));
            tmp.setStuWorkOfficeId(item.getId());
            tmp.setStatus(statusMap.get(item.getAdminId()) != 0);
            return tmp;
        }).collect(Collectors.toList());
    }

    @Override
    public Boolean importStuInfo(MultipartFile multipartFile) {
        if (multipartFile.isEmpty()) {
            throw new ApiException("文件为空");
        }
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        // 这里每次会读取3000条数据 然后返回过来 直接调用使用数据就行
        try {
            Long adminId = MyUtil.getCurrentAdminId();
            //取出cpu核心数作为一次处理数据的条数
            int processors = Runtime.getRuntime().availableProcessors();
            //启动size个线程的线程池
            ExecutorService threadPool = Executors.newFixedThreadPool(processors);
            EasyExcel.read(multipartFile.getInputStream(), StudentImportDto.class, new PageReadListener<StudentImportDto>(dataList -> {
                //计数器
                CountDownLatch countDownLatch = new CountDownLatch(dataList.size());
                dataList.forEach(studentImportDto -> {
                    threadPool.execute(() -> {
                        try {
                            this.saveStuInfo(studentImportDto, adminId);
                        } catch (Exception e) {
                            throw new ApiException(e);
                        } finally {
                            countDownLatch.countDown();
                        }
                    });
                });
            })).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }

    @Transactional
    @Override
    public void saveStuInfo(StudentImportDto studentImportDto, Long adminId) {
        if (studentImportDto.getJobNumber() == null) return;

        // 绑定学院
        TbCollege findCollege = collegeService.getOne(new QueryWrapper<TbCollege>().eq("college_name", studentImportDto.getCollegeName()));
        if (findCollege == null) {
            Boolean isCollageLocked = redisTemplate.opsForValue().setIfAbsent("importCollageLock:" + studentImportDto.getCollegeName(), "1", 5, TimeUnit.MINUTES);
            if (isCollageLocked) {
                findCollege = new TbCollege();
                findCollege.setCollegeName(studentImportDto.getCollegeName());
                findCollege.setStuWorkOfficeId(adminId);
                findCollege.setUpdateTime(LocalDateTime.now());
                findCollege.setCreateTime(LocalDateTime.now());
                collegeService.save(findCollege);
                redisTemplate.delete("importCollageLock:" + studentImportDto.getCollegeName());
            }
            else{
                while(!isCollageLocked){
                    isCollageLocked = redisTemplate.opsForValue().setIfAbsent("importCollageLock:" + studentImportDto.getCollegeName(), "1", 5, TimeUnit.MINUTES);
                }
                redisTemplate.delete("importCollageLock:" + studentImportDto.getCollegeName());
                findCollege = collegeService.getOne(new QueryWrapper<TbCollege>().eq("college_name", studentImportDto.getCollegeName()));
            }
        }

        // 绑定专业
        String className = studentImportDto.getClassDesc();
        TbMajor findMajor = null;
        String majorName = null;
        if (className.matches("对口")){
            majorName = studentImportDto.getMajorName() + "(对口)";
            findMajor = majorService.getOne(new QueryWrapper<TbMajor>().eq("major_name", majorName));
        } else {
            majorName = studentImportDto.getMajorName();
            findMajor = majorService.getOne(new QueryWrapper<TbMajor>().eq("major_name", majorName));
        }
        if (findMajor == null) {
            Boolean isMajorNameLocked = redisTemplate.opsForValue().setIfAbsent("importMajorLock:" + majorName, "1", 5, TimeUnit.MINUTES);
            if (isMajorNameLocked) {
                // 无专业创建一个
                findMajor = new TbMajor();
                findMajor.setCollegeId(findCollege.getId());
                findMajor.setMajorName(majorName);
                findMajor.setUpdateTime(LocalDateTime.now());
                findMajor.setCreateTime(LocalDateTime.now());
                majorService.save(findMajor);
                redisTemplate.delete("importMajorLock:" + majorName);
            }
            else {
                while(!isMajorNameLocked){
                    isMajorNameLocked =  redisTemplate.opsForValue().setIfAbsent("importMajorLock:" + majorName, "1", 5, TimeUnit.MINUTES);
                }
                redisTemplate.delete("importMajorLock:" + majorName);
                findMajor = majorService.getOne(new QueryWrapper<TbMajor>().eq("major_name", majorName));
            }
        }

        // 查询年级是否存在
        String gradeNumber = className.substring(2, 6);
        TbGrade findGrade = iTbGradeService.getOne(new QueryWrapper<TbGrade>().eq("number", gradeNumber));
        if (findGrade == null) {
            Boolean isGradeLocked = redisTemplate.opsForValue().setIfAbsent("importGradeLock:" + gradeNumber, "1", 5, TimeUnit.MINUTES);
            if (isGradeLocked) {
                findGrade = new TbGrade();
                findGrade.setNumber(Integer.parseInt(gradeNumber));
                findGrade.setUpdateTime(LocalDateTime.now());
                findGrade.setCreateTime(LocalDateTime.now());
                iTbGradeService.save(findGrade);
                redisTemplate.delete("importGradeLock:" + gradeNumber);
            }
            else{
                while(!isGradeLocked){
                    isGradeLocked = redisTemplate.opsForValue().setIfAbsent("importGradeLock:" + gradeNumber, "1", 5, TimeUnit.MINUTES);
                }
                redisTemplate.delete("importGradeLock:" + gradeNumber);
                findGrade = iTbGradeService.getOne(new QueryWrapper<TbGrade>().eq("number", gradeNumber));
            }
        }

        // 查询班级是否存在
        char classNumber = className.charAt(6);
        TbClass findClass = classService.getOne(new QueryWrapper<TbClass>().eq("description", studentImportDto.getClassDesc()));
        if (findClass == null) {
            Boolean isClassLocked = redisTemplate.opsForValue().setIfAbsent("importClassLock:" + studentImportDto.getClassDesc(), "1", 5, TimeUnit.MINUTES);
            if (isClassLocked) {
                findClass = new TbClass();
                findClass.setGradeId(findGrade.getId());
                findClass.setClassNumber(Character.isDigit(classNumber) ? Integer.parseInt(String.valueOf(classNumber)) : null);
                findClass.setMajorId(findMajor.getId());
                findClass.setDescription(studentImportDto.getClassDesc());
                findClass.setUpdateTime(LocalDateTime.now());
                findClass.setCreateTime(LocalDateTime.now());
                classService.save(findClass);
                redisTemplate.delete("importClassLock:" + studentImportDto.getClassDesc());
            }
            else{
                // 循环拿到锁
                while(!isClassLocked){
                    isClassLocked = redisTemplate.opsForValue().setIfAbsent("importClassLock:" + studentImportDto.getClassDesc(), "1", 5, TimeUnit.MINUTES);
                }
                redisTemplate.delete("importClassLock:" + studentImportDto.getClassDesc());
                findClass = classService.getOne(new QueryWrapper<TbClass>().eq("description", studentImportDto.getClassDesc()));
            }
        }

        // 查找用户是否存在
        TbStudent findStu = studentService.getOne(new QueryWrapper<TbStudent>().eq("job_number", studentImportDto.getJobNumber()));
        if (findStu == null) {
            // 保存学生信息
            StudentAdd studentAdd = new StudentAdd();
            studentAdd.setName(studentImportDto.getName());
            studentAdd.setJobNumber(studentImportDto.getJobNumber());
            studentAdd.setGender(studentImportDto.getGender().equals("男"));
            studentAdd.setPassword(studentImportDto.getJobNumber().substring(studentImportDto.getJobNumber().length() - 6));
            studentAdd.setClassId(findClass.getId());

            studentService.add(studentAdd);
            findStu = studentService.getOne(new QueryWrapper<TbStudent>().eq("job_number", studentImportDto.getJobNumber()));
        }

        // 保存今年的绩点记录
        GpaRecordAdd gpaRecordAdd = new GpaRecordAdd();
        gpaRecordAdd.setPass(studentImportDto.getPass().equals("100%"));
        gpaRecordAdd.setScoreGpa(studentImportDto.getScoreGpa());
        gpaRecordAdd.setStandardId(1L);
        gpaRecordAdd.setMajorId(findMajor.getId());
        gpaRecordAdd.setStuId(findStu.getId());
        iTbGpaRecordService.addRecord(gpaRecordAdd);

    }
}
